﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

namespace EmitDebugging.Tests
{
	[TestClass]
	public sealed class MethodBaseDebuggingTests : AssemblyCreationTests
	{
		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterBeginCatchBlock()
		{
			MethodBaseDebuggingTests.GetMethod().BeginCatchBlock(null);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterBeginExceptFilterBlock()
		{
			MethodBaseDebuggingTests.GetMethod().BeginExceptFilterBlock();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterBeginExceptionBlock()
		{
			MethodBaseDebuggingTests.GetMethod().BeginExceptionBlock();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterBeginFaultBlock()
		{
			MethodBaseDebuggingTests.GetMethod().BeginFaultBlock();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterBeginFinallyBlock()
		{
			MethodBaseDebuggingTests.GetMethod().BeginFinallyBlock();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterBeginScope()
		{
			MethodBaseDebuggingTests.GetMethod().BeginScope();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterDeclareLocalType()
		{
			MethodBaseDebuggingTests.GetMethod().DeclareLocal(typeof(int));
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterDeclareLocalTypeBool()
		{
			MethodBaseDebuggingTests.GetMethod().DeclareLocal(typeof(int), true);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterDefineLabel()
		{
			MethodBaseDebuggingTests.GetMethod().DefineLabel();
		}

		[TestMethod]
		public void CallDisposeAfterDispose()
		{
			MethodBaseDebuggingTests.GetMethod().Dispose();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCode()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeByte()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, (byte)33);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeConstructorInfo()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as ConstructorInfo);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeDouble()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, 33d);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeFieldInfo()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as FieldInfo);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeFloat()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, 33f);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeInt()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, 33);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeLabel()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, new Label());
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeLabelArray()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, new Label[3]);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeLocalBuilder()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as LocalBuilder);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeLong()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, 33L);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeMethodInfo()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as MethodInfo);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeShort()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, (short)33);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeSignatureHelper()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as SignatureHelper);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeString()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as string);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitOpCodeType()
		{
			MethodBaseDebuggingTests.GetMethod().Emit(OpCodes.Ret, null as Type);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitCall()
		{
			MethodBaseDebuggingTests.GetMethod().EmitCall(
				OpCodes.Calli, null as MethodInfo, null as Type[]);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitCalliManaged()
		{
			MethodBaseDebuggingTests.GetMethod().EmitCalli(
				OpCodes.Calli, CallingConventions.Standard, null as Type, null as Type[], null as Type[]);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitCalliUnmanaged()
		{
			MethodBaseDebuggingTests.GetMethod().EmitCalli(
				OpCodes.Calli, CallingConvention.StdCall, null as Type, null as Type[]);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitWriteLineFieldInfo()
		{
			MethodBaseDebuggingTests.GetMethod().EmitWriteLine(null as FieldInfo);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitWriteLineLocalBuilder()
		{
			MethodBaseDebuggingTests.GetMethod().EmitWriteLine(null as LocalBuilder);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEmitWriteLineString()
		{
			MethodBaseDebuggingTests.GetMethod().EmitWriteLine(null as string);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEndExceptionBlock()
		{
			MethodBaseDebuggingTests.GetMethod().EndExceptionBlock();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterEndScope()
		{
			MethodBaseDebuggingTests.GetMethod().EndScope();
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterMarkLabel()
		{
			MethodBaseDebuggingTests.GetMethod().MarkLabel(new Label());
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterThrowException()
		{
			MethodBaseDebuggingTests.GetMethod().ThrowException(null);
		}

		[TestMethod, ExpectedException(typeof(ObjectDisposedException))]
		public void CallDisposeAfterUsingNamespace()
		{
			MethodBaseDebuggingTests.GetMethod().UsingNamespace(null);
		}

		private static MethodDebugging GetMethod()
		{
			MethodDebugging method = null;

			var name = Guid.NewGuid().ToString("N");

			using(var assembly = AssemblyCreationTests.CreateDebuggingAssembly(name))
			{
				using(var type = AssemblyCreationTests.CreateDebuggingType(
					assembly, assembly.Builder.GetDynamicModule(name), "Type"))
				{
					using(method = type.GetMethodDebugging(
						type.Builder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static)))
					{
					}
				}
			}

			return method;
		}
	}
}
